package cn.sylinx.horm.transaction.jdbc;

import java.sql.Connection;
import java.sql.SQLException;
import cn.sylinx.horm.core.common.TransInfo;
import cn.sylinx.horm.core.datasource.DataSourceConnectionProvicer;
import cn.sylinx.horm.core.datasource.NamedDataSource;
import cn.sylinx.horm.core.datasource.TransactionalConnectionProvider;
import cn.sylinx.horm.exception.TransactionException;
import cn.sylinx.horm.util.GLog;

public class JdbcTransactionalConnectionProvider extends DataSourceConnectionProvicer
        implements TransactionalConnectionProvider {

    private final ThreadLocal<TransInfo> transInfos = new ThreadLocal<>();
    private final ThreadLocal<Connection> transConnections = new ThreadLocal<>();

    public JdbcTransactionalConnectionProvider() {
    }

    public JdbcTransactionalConnectionProvider(NamedDataSource namedDataSource) {
        super(namedDataSource);
    }

    @Override
    public Connection getConnection() throws SQLException {
        if (isInTransaction()) {
            Connection conn = transConnections.get();
            if (conn == null) {
                conn = getConnectionFromDataSource();
                conn.setAutoCommit(false);
                conn.setTransactionIsolation(transInfos.get().getTransactionIsolation());
                transConnections.set(conn);
            }
            return conn;
        } else {
            return getConnectionFromDataSource();
        }
    }

    @Override
    public void releaseConnection(Connection conn) throws SQLException {
        if (isInTransaction()) {
            // 事务期间链接不关闭
            return;
        }
        if (conn != null) {
            conn.close();
        }
    }

    @Override
    public void startTransaction() {
        transInfos.set(new TransInfo());
    }

    @Override
    public void startTransaction(int isolationLevel) {
        transInfos.set(new TransInfo(isolationLevel));
    }

    @Override
    public boolean isInTransaction() {
        return transInfos.get() != null;
    }

    @Override
    public void commitTransaction() throws TransactionException {
        try {
            commitTransactionInner();
        } finally {
            clear();
        }
    }

    public void commitTransactionInner() {
        if (!isInTransaction()) {
            // 非事务
            throw new TransactionException("CAN NOT commit transaction in non transactional env");
        }
        Connection conn = transConnections.get();
        if (conn == null) {
            throw new TransactionException("illegal state");
        }
        SQLException ex = null;
        try {
            conn.commit();
        } catch (SQLException e) {
            ex = e;
        } finally {
            try {
                if (!conn.isClosed()) {
                    // 关闭前恢复为自动提交，因为是使用连接池，连接并没关闭，会影响后续使用
                    conn.setAutoCommit(true);
                    conn.close();
                }
            } catch (SQLException ex1) {
                GLog.error("commit error of connection " + conn + " " + ex1.getMessage());
            }
        }
        if (ex != null) {
            throw new TransactionException(ex);
        }
    }

    @Override
    public void rollbackTransaction() {
        try {
            rollbackTransactionInner();
        } finally {
            clear();
        }
    }

    private void rollbackTransactionInner() {
        if (!isInTransaction()) {
            // 非事务
            throw new TransactionException("NOT transaction");
        }
        Connection conn = transConnections.get();
        if (conn == null) {
            throw new TransactionException("illegal state");
        }
        SQLException e = null;
        try {
            conn.rollback();
        } catch (SQLException ex) {
            e = ex;
        } finally {
            try {
                if (!conn.isClosed()) {
                    // 关闭前恢复为自动提交，因为是使用连接池，连接并没关闭，会影响后续使用
                    conn.setAutoCommit(true);
                    conn.close();
                }
            } catch (SQLException ex) {
                GLog.error("rollback error of connection " + conn + " " + ex.getMessage());
            }
        }
        if (e != null) {
            throw new TransactionException(e);
        }
    }

    public void clear() {
        transConnections.remove();
        transInfos.remove();
    }
}